package com.owner.lt.tool.utils;

import java.io.*;
import java.net.URL;
import java.nio.CharBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;

public class IOUtils {

    public static byte[] getBytes() {
        return new byte[8192];
    }

    public static char[] getChars() {
        return new char[8192];
    }

    public static byte[] byteArray(final int size) {
        return new byte[size];
    }

    private static char[] charArray(final int size) {
        return new char[size];
    }

    public static void skipFully(final InputStream input, final long toSkip) throws IOException {
        if (toSkip < 0) {
            throw new IllegalArgumentException("Bytes to skip must not be negative: " + toSkip);
        }
        final long skipped = skip(input, toSkip);
        if (skipped != toSkip) {
            throw new EOFException("Bytes to skip: " + toSkip + " actual: " + skipped);
        }
    }

    public static long skip(final Reader reader, final long toSkip) throws IOException {
        if (toSkip < 0) {
            throw new IllegalArgumentException("Skip count must be non-negative, actual: " + toSkip);
        }
        long remain = toSkip;
        while (remain > 0) {
            // See https://issues.apache.org/jira/browse/IO-203 for why we use read() rather than delegating to skip()
            final char[] charArray = getChars();
            final long n = reader.read(charArray, 0, (int) Math.min(remain, charArray.length));
            if (n < 0) { // EOF
                break;
            }
            remain -= n;
        }
        return toSkip - remain;
    }

    public static void skipFully(final Reader reader, final long toSkip) throws IOException {
        final long skipped = skip(reader, toSkip);
        if (skipped != toSkip) {
            throw new EOFException("Chars to skip: " + toSkip + " actual: " + skipped);
        }
    }

    public static long skip(final InputStream input, final long toSkip) throws IOException {
        if (toSkip < 0) {
            throw new IllegalArgumentException("Skip count must be non-negative, actual: " + toSkip);
        }
        /*
         * N.B. no need to synchronize access to SKIP_BYTE_BUFFER: - we don't care if the buffer is created multiple
         * times (the data is ignored) - we always use the same size buffer, so if it it is recreated it will still be
         * OK (if the buffer size were variable, we would need to synch. to ensure some other thread did not create a
         * smaller one)
         */
        long remain = toSkip;
        while (remain > 0) {
            // See https://issues.apache.org/jira/browse/IO-203 for why we use read() rather than delegating to skip()
            final byte[] byteArray = getBytes();
            final long n = input.read(byteArray, 0, (int) Math.min(remain, byteArray.length));
            if (n < 0) { // EOF
                break;
            }
            remain -= n;
        }
        return toSkip - remain;
    }

    public static Charset toCharset(final String charsetName) throws UnsupportedCharsetException {
        return charsetName == null ? Charset.defaultCharset() : Charset.forName(charsetName);
    }

    public static void closeQuietly(Closeable... closeables) {
        if (closeables != null) {
            for (Closeable closeable : closeables) {
                closeQuietly(closeable);
            }
        }
    }

    public static void closeQuietly(Collection<? extends Closeable> closeables) {
        if (closeables != null) {
            for (Closeable closeable : closeables) {
                closeQuietly(closeable);
            }
        }
    }

    public static void closeQuietly(Closeable closeable) {
        closeQuietly(closeable, null);
    }

    // 指定消费者处理IO异常
    public static void closeQuietly(Closeable closeable, Consumer<IOException> consumer) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (IOException var3) {
                if (consumer != null) {
                    consumer.accept(var3);
                }
            }
        }

    }

    public static boolean contentEquals(InputStream input1, InputStream input2) throws IOException {
        if (input1 == input2) {
            return true;
        } else if (input1 != null && input2 != null) {
            byte[] array1 = getBytes();
            byte[] array2 = getBytes();

            while(true) {
                int pos1 = 0;
                int pos2 = 0;

                for(int index = 0; index < 8192; ++index) {
                    if (pos1 == index) {
                        int count1;
                        do {
                            count1 = input1.read(array1, pos1, 8192 - pos1);
                        } while(count1 == 0);

                        if (count1 == -1) {
                            return pos2 == index && input2.read() == -1;
                        }

                        pos1 += count1;
                    }

                    if (pos2 == index) {
                        int count2;
                        do {
                            count2 = input2.read(array2, pos2, 8192 - pos2);
                        } while(count2 == 0);

                        if (count2 == -1) {
                            return pos1 == index && input1.read() == -1;
                        }

                        pos2 += count2;
                    }

                    if (array1[index] != array2[index]) {
                        return false;
                    }
                }
            }
        } else {
            return false;
        }
    }

    public static boolean contentEquals(Reader input1, Reader input2) throws IOException {
        if (input1 == input2) {
            return true;
        } else if (input1 != null && input2 != null) {
            char[] array1 = getChars();
            char[] array2 = getChars();

            while(true) {
                int pos1 = 0;
                int pos2 = 0;

                for(int index = 0; index < 8192; ++index) {
                    if (pos1 == index) {
                        int count1;
                        do {
                            count1 = input1.read(array1, pos1, 8192 - pos1);
                        } while(count1 == 0);

                        if (count1 == -1) {
                            return pos2 == index && input2.read() == -1;
                        }

                        pos1 += count1;
                    }

                    if (pos2 == index) {
                        int count2;
                        do {
                            count2 = input2.read(array2, pos2, 8192 - pos2);
                        } while(count2 == 0);

                        if (count2 == -1) {
                            return pos1 == index && input1.read() == -1;
                        }

                        pos2 += count2;
                    }

                    if (array1[index] != array2[index]) {
                        return false;
                    }
                }
            }
        } else {
            return false;
        }
    }


    public static int copy(InputStream inputStream, OutputStream outputStream) throws IOException {
        long count = copyLarge(inputStream, outputStream);
        return count > 2147483647L ? -1 : (int)count;
    }

    public static long copy(InputStream inputStream, OutputStream outputStream, int bufferSize) throws IOException {
        return copyLarge(inputStream, outputStream, byteArray(bufferSize));
    }

    /** @deprecated */
    @Deprecated
    public static void copy(InputStream input, Writer writer) throws IOException {
        copy(input, writer, Charset.defaultCharset());
    }

    public static void copy(InputStream input, Writer writer, Charset charset) throws IOException {
        InputStreamReader reader = new InputStreamReader(input, charset == null ? Charset.defaultCharset() : charset);
        copy((Reader)reader, (Writer)writer);
    }

    public static void copy(InputStream input, Writer writer, String inputCharsetName) throws IOException {
        copy(input, writer, toCharset(inputCharsetName));
    }

    public static long copy(Reader reader, Appendable output) throws IOException {
        return copy(reader, output, CharBuffer.allocate(8192));
    }

    public static long copy(Reader reader, Appendable output, CharBuffer buffer) throws IOException {
        long count;
        int n;
        for(count = 0L; -1 != (n = reader.read(buffer)); count += (long)n) {
            buffer.flip();
            output.append(buffer, 0, n);
        }

        return count;
    }

    /** @deprecated */
    @Deprecated
    public static void copy(Reader reader, OutputStream output) throws IOException {
        copy(reader, output, Charset.defaultCharset());
    }

    public static void copy(Reader reader, OutputStream output, Charset charset) throws IOException {
        OutputStreamWriter writer = new OutputStreamWriter(output, charset == null ? Charset.defaultCharset() : charset);
        copy((Reader)reader, (Writer)writer);
        writer.flush();
    }

    public static void copy(Reader reader, OutputStream output, String outputCharsetName) throws IOException {
        copy(reader, output, toCharset(outputCharsetName));
    }

    public static int copy(Reader reader, Writer writer) throws IOException {
        long count = copyLarge(reader, writer);
        return count > 2147483647L ? -1 : (int)count;
    }

    public static long copy(URL url, File file) throws IOException {
        OutputStream outputStream = Files.newOutputStream(((File) Objects.requireNonNull(file, "file")).toPath());
        Throwable var3 = null;

        long var4;
        try {
            var4 = copy(url, outputStream);
        } catch (Throwable var14) {
            var3 = var14;
            throw var14;
        } finally {
            if (outputStream != null) {
                if (var3 != null) {
                    try {
                        outputStream.close();
                    } catch (Throwable var13) {
                        var3.addSuppressed(var13);
                    }
                } else {
                    outputStream.close();
                }
            }

        }

        return var4;
    }

    public static long copy(URL url, OutputStream outputStream) throws IOException {
        InputStream inputStream = ((URL)Objects.requireNonNull(url, "url")).openStream();
        Throwable var3 = null;

        long var4;
        try {
            var4 = copyLarge(inputStream, outputStream);
        } catch (Throwable var14) {
            var3 = var14;
            throw var14;
        } finally {
            if (inputStream != null) {
                if (var3 != null) {
                    try {
                        inputStream.close();
                    } catch (Throwable var13) {
                        var3.addSuppressed(var13);
                    }
                } else {
                    inputStream.close();
                }
            }

        }

        return var4;
    }

    public static long copyLarge(InputStream inputStream, OutputStream outputStream) throws IOException {
        return copy(inputStream, outputStream, 8192);
    }

    public static long copyLarge(InputStream inputStream, OutputStream outputStream, byte[] buffer) throws IOException {
        Objects.requireNonNull(inputStream, "inputStream");
        Objects.requireNonNull(outputStream, "outputStream");

        long count;
        int n;
        for(count = 0L; -1 != (n = inputStream.read(buffer)); count += (long)n) {
            outputStream.write(buffer, 0, n);
        }

        return count;
    }

    public static long copyLarge(InputStream input, OutputStream output, long inputOffset, long length) throws IOException {
        return copyLarge(input, output, inputOffset, length, getBytes());
    }

    public static long copyLarge(InputStream input, OutputStream output, long inputOffset, long length, byte[] buffer) throws IOException {
        if (inputOffset > 0L) {
            skipFully(input, inputOffset);
        }

        if (length == 0L) {
            return 0L;
        } else {
            int bufferLength = buffer.length;
            int bytesToRead = bufferLength;
            if (length > 0L && length < (long)bufferLength) {
                bytesToRead = (int)length;
            }

            long totalRead = 0L;

            int read;
            while(bytesToRead > 0 && -1 != (read = input.read(buffer, 0, bytesToRead))) {
                output.write(buffer, 0, read);
                totalRead += (long)read;
                if (length > 0L) {
                    bytesToRead = (int)Math.min(length - totalRead, (long)bufferLength);
                }
            }

            return totalRead;
        }
    }

    public static long copyLarge(Reader reader, Writer writer) throws IOException {
        return copyLarge(reader, writer, getChars());
    }

    public static long copyLarge(Reader reader, Writer writer, char[] buffer) throws IOException {
        long count;
        int n;
        for(count = 0L; -1 != (n = reader.read(buffer)); count += (long)n) {
            writer.write(buffer, 0, n);
        }

        return count;
    }

    public static long copyLarge(Reader reader, Writer writer, long inputOffset, long length) throws IOException {
        return copyLarge(reader, writer, inputOffset, length, getChars());
    }

    public static long copyLarge(Reader reader, Writer writer, long inputOffset, long length, char[] buffer) throws IOException {
        if (inputOffset > 0L) {
            skipFully(reader, inputOffset);
        }

        if (length == 0L) {
            return 0L;
        } else {
            int bytesToRead = buffer.length;
            if (length > 0L && length < (long)buffer.length) {
                bytesToRead = (int)length;
            }

            long totalRead = 0L;

            int read;
            while(bytesToRead > 0 && -1 != (read = reader.read(buffer, 0, bytesToRead))) {
                writer.write(buffer, 0, read);
                totalRead += (long)read;
                if (length > 0L) {
                    bytesToRead = (int)Math.min(length - totalRead, (long)buffer.length);
                }
            }

            return totalRead;
        }
    }

}
